The R package decorr is grounded on graph theory and spatial analysis. It offers concepts and functions to model Prehistorical iconographical composition and to prepare further analysis (clustering, Harris diagram, etc.) in order to contribute to cross-cultural iconography comparisons studies by a higher normalization of quantitative analysis (Alexander 2008; Huet and Alexander 2015; Huet 2018).
The main principle of decorr package is to considerate any iconographic compositions (here, ‘decorations’) as geometric graph of graphical units (GUs). This geometric graph is also known as planar graph or spatialised graph. The GUs are decorated surfaces (POLYGONS) modelised as nodes (POINTS) sharing proximity links (edges, LINES) one with another when their Voronoi cell share a border (birel: touches).
GIS view of the Cerro Muriano 1 stelae with the Voronoi cells of its GUs, GUs (nodes) and proximity links between these GUs (edges)
Graph theory offers a conceptual framework and indices (global at the entire graph scale, local at the vertex scale) to deal with notions of networks, relationships and neighbourhoods. The graph is commonly built within a GIS interface. Indeed, use of GIS allows to consitute a spatial database of the decoration’s iconographic contents and facilitates data recording and visualization: snapping options for example to connect GUs (nodes) with lines (edges), features symbology, layer transparency, etc.
The development version of decorr package and its vignette can be download from GitHub
devtools::install_github("zoometh/iconr", build_vignettes=TRUE)
The R package decorr is composed by functions and a dataset example. The main packages used by the decorr package are:
library(decorr)
The training dataset are drawings with their listing and data for nodes, edges. The current path of training dataset is the exdata/ folder. All the input data (dataframes, shapefiles, images) start with a letter (regex "^[[:alpha:]]") while the output data start with a punctuation (regex"^[[:punct:]]") or a numerical (regex "^[[:digit:]]")
# dataDir <- "C:/Documents/decorr/inst/extdata/"
dataDir <- system.file("extdata", package = "decorr")
cat(grep("^[[:alpha:]]",list.files(dataDir),value=T), sep="\n")
> Brozas.Brozas.jpg
> Cerro_Muriano.Cerro_Muriano_1.jpg
> edges.csv
> edges.dbf
> edges.shp
> edges.shx
> edges.tsv
> gis.png
> Ibahernando.Ibahernando.jpg
> imgs.csv
> imgs.tsv
> nodes.csv
> nodes.dbf
> nodes.shp
> nodes.shx
> nodes.tsv
> Torrejon_Rubio.Torrejon_Rubio_1.jpg
> Zarza_de_Montanchez.Zarza_de_Montanchez.jpg
Each decoration is identified by its the name and the name of the site it belongs. Each decoration is a set of:
images (.jpg, .png, .jpeg, .tiff, .pdf, etc.)
decoration identifiers dataframe (.tsv or .csv)
imgs_path <- paste0(dataDir, "/imgs.csv")
imgs <- read.table(imgs_path, sep=";", stringsAsFactors = FALSE)
.tsv, .csv or .shp)Since a GIS interface is often the most practicale to record graph nodes and graph edges with POINTS and LINES geometries coming from shapefiles (.shp)
nodes_path <- paste0(dataDir, "/nodes.shp")
nodes.shp <- rgdal::readOGR(dsn = nodes_path, layer = "nodes", verbose = F)
nodes <- as.data.frame(nodes.shp)
edges_path <- paste0(dataDir, "/edges.shp")
edges.shp <- rgdal::readOGR(dsn = edges_path, layer = "edges", verbose = F)
edges <- as.data.frame(edges.shp)
Nodes and edges can also be recorded in tabular format: .tsv (tab-separated values) or .csv (semicolons-separated values)
nodes_path <- paste0(dataDir, "/nodes.tsv")
edges_path <- paste0(dataDir, "/edges.tsv")
nodes <- read.table(nodes_path, sep="\t", stringsAsFactors = FALSE)
edges <- read.table(edges_path, sep="\t", stringsAsFactors = FALSE)
The list of graph decorations is created with the list_dec() function. These graphs are igraph objects
lgrph <- list_dec(imgs, nodes, edges)
g <- lgrph[[1]]
as.character(class(g))
> [1] "igraph"
By default, the plot.igraph() function (ie, igraph::plot()) spazialisation (layout) is based on x and y columns, when exist. This is our case since we work with geometric graphs. But very different layouts exist for the same graphs (graph drawing)
par(mfrow=c(1, 2))
coords <- layout.fruchterman.reingold(lgrph[[1]])
plot(g,
vertex.size = 20,
vertex.frame.color="white",
vertex.label.family = "sans",
vertex.label.cex = .8,
)
title("graph drawing on x, y coordinates", cex.main=1, font.main= 1)
plot(g,
layout = coords,
vertex.size = degree(g)*10,
vertex.frame.color="white",
vertex.label.family = "sans",
vertex.label.cex = .8,
)
title(paste0("force-directed graph drawing,",
" \n size of nodes depending on their degrees"),
cex.main=1, font.main= 1)
mtext(lgrph[[1]]$decor, cex = 1.2, side = 3, line = -20, outer = TRUE)
Images or drawings (ie, images, raster, grid) accepted are common formats (jpg, png, jpeg, tiff, pdf, etc.). The current images of the training dataset come from a PhD tesis, published by M. Diaz-Guardamino (Dı́az-Guardamino Uribe 2010)
imgs_path <- paste0(dataDir, "/imgs.tsv")
imgs <- read.table(imgs_path, sep="\t", stringsAsFactors = FALSE)
knitr::kable(imgs, "html") %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12)
| idf | site | decor | img |
|---|---|---|---|
| 1 | Cerro Muriano | Cerro Muriano 1 | Cerro_Muriano.Cerro_Muriano_1.jpg |
| 2 | Torrejon Rubio | Torrejon Rubio 1 | Torrejon_Rubio.Torrejon_Rubio_1.jpg |
| 3 | Brozas | Brozas | Brozas.Brozas.jpg |
| 4 | Zarza de Montanchez | Zarza De Montanchez | Zarza_de_Montanchez.Zarza_De_Montanchez.jpg |
| 5 | Ibahernando | Ibahernando | Ibahernando.Ibahernando.jpg |
The decorations unique identifiers are the concatenation of the site name and decoration name. For example, the name of the Cerrano Muriano 1 decoration is Cerro_Muriano.Cerro_Muriano_1.jpg. For a given decoration, its image is the space of reference of the graph: nodes and edges inherit their coordinates from the image grid. Identifiers and paths to images are stored in the imgs dataframe
library(magick)
dataDir <- paste0(system.file("extdata", package = "decorr"))
imgs_path <- paste0(dataDir, "/imgs.csv")
imgs <- read.table(imgs_path, sep=";", stringsAsFactors = FALSE)
cm1 <- image_read(paste0(dataDir, "/", imgs[1,"img"]))
W <- as.character(image_info(cm1)$width)
H <- as.character(image_info(cm1)$height)
o <- "0"
image_border(image = cm1, "#808080", "2x2") %>%
image_annotate(text = paste0(o, ", ", o),
size = 30,
gravity = "northwest") %>%
image_annotate(text = paste0(W, ", ", o),
size = 30,
gravity = "northeast") %>%
image_annotate(text = paste0(o, ", ", H),
size = 30,
gravity = "southwest") %>%
image_annotate(text = paste0(W, ", ", H),
size = 30,
gravity = "southeast")
Cerro_Muriano.Cerro_Muriano_1.jpg with the coordinates of its corners
Nodes are stored in a dataframe (.csv or .tsv) or a shapefile (.shp). If the input data comes from a dataframe (.csv or .tsv), coordinates columns x and y are required
nds.df <- read_nds(site = "Cerro Muriano", decor = "Cerro Muriano 1", doss = dataDir)
knitr::kable(nds.df, "html") %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12)
| site | decor | id | type | x | y |
|---|---|---|---|---|---|
| Cerro Muriano | Cerro Muriano 1 | 1 | personnage | 349.8148 | -298.3244 |
| Cerro Muriano | Cerro Muriano 1 | 2 | casque | 349.8148 | -243.9851 |
| Cerro Muriano | Cerro Muriano 1 | 3 | lance | 238.4637 | -298.3244 |
| Cerro Muriano | Cerro Muriano 1 | 4 | bouclier | 446.0222 | -381.1697 |
| Cerro Muriano | Cerro Muriano 1 | 5 | peigne | 283.0041 | -358.0086 |
| Cerro Muriano | Cerro Muriano 1 | 7 | sexe_masculin | 342.6884 | -427.4917 |
| Cerro Muriano | Cerro Muriano 1 | 8 | lingot_pdb | 451.1489 | -237.4782 |
While, in theory, the nodes are the exacts centroids of each GU, in a more handy manner they can be located manually near to these centroids.
column names
site: decoration site
decor: decoration name
id: id of the edges (a unique number)
type: type of the nodes
x, y: coordinates of the nodes
Edges are stored in a dataframe (.csv or .tsv) or a shapefile (.shp).
edges <- read.table(edges_path, sep="\t", stringsAsFactors = FALSE)
knitr::kable(head(edges), "html") %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12) %>%
gsub("\\+", "$$+$$", .)
| site | decor | a | b | type |
|---|---|---|---|---|
| Cerro Muriano | Cerro Muriano 1 | 1 | 4 | = |
| Cerro Muriano | Cerro Muriano 1 | 1 | 5 | = |
| Cerro Muriano | Cerro Muriano 1 | 3 | 5 | = |
| Cerro Muriano | Cerro Muriano 1 | 1 | 2 | \[+\] |
| Cerro Muriano | Cerro Muriano 1 | 1 | 7 | \[+\] |
| Cerro Muriano | Cerro Muriano 1 | 3 | 1 | = |
Edges geometries are calculated or recalculated from nodes geometries with a join between id nodes’ field and the a and b edges’ fields where a is the starting node of the edges (columns xa and ya) and b is the ending node of the edges (columns xa and ya).
eds.df <- read_eds(site = "Cerro Muriano", decor = "Cerro Muriano 1", doss = dataDir)
knitr::kable(head(eds.df), "html") %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12) %>%
gsub("\\+", "$$+$$", .)
| site | decor | a | b | type | xa | ya | xb | yb |
|---|---|---|---|---|---|---|---|---|
| Cerro Muriano | Cerro Muriano 1 | 1 | 4 | = | 349.8148 | -298.3244 | 446.0222 | -381.1697 |
| Cerro Muriano | Cerro Muriano 1 | 1 | 5 | = | 349.8148 | -298.3244 | 283.0041 | -358.0086 |
| Cerro Muriano | Cerro Muriano 1 | 3 | 5 | = | 238.4637 | -298.3244 | 283.0041 | -358.0086 |
| Cerro Muriano | Cerro Muriano 1 | 1 | 2 | \[+\] | 349.8148 | -298.3244 | 349.8148 | -243.9851 |
| Cerro Muriano | Cerro Muriano 1 | 1 | 7 | \[+\] | 349.8148 | -298.3244 | 342.6884 | -427.4917 |
| Cerro Muriano | Cerro Muriano 1 | 3 | 1 | = | 238.4637 | -298.3244 | 349.8148 | -298.3244 |
column names
site : decoration site
decor : decoration name
id : id of the edge (a unique number)
a : id of the first node (see, nodes)
b : id of the second node (see, nodes)
type : edges types
= : normal edges between contemporaneous nodes (undirected edge)
+ : attribute edges, between contemporaneous nodes where the node b is an attribute of node a (directed edge)
> : overlap edges, between non-contemporaneous nodes where the node a overlaps node b (directed edge)
xa, ya: coordinates of the starting node, or main node, or overlapping node (a)
xb, yb: coordinates of the ending node, or attribute node, or overlapped node (b)
As stated by the graph theory, edges can be undirected or directed. In the decorr package, by default:
all comtemporaneous nodes have normal edges or attribute edges edges displayed in orange
all non-comtemporaneous nodes have overlap edges and displayed in blue edges (see contemporaneous nodes)
The named_elements() function allow to display the textual notation of the different types of edges (-=-, -+- or ->-)
named_elements(lgrph[[1]], focus = "edges", nd.var="type")[1]
> [1] "bouclier-=-personnage"
When there is nodes with the same nd.var, this function add the suffix # to the nd.var in order to desambiguate the nodes list. This is the case, for example, for the chariot_char-+-cheval (x2) and chariot_char-+-roue (x2) of the Zarza de Montsanchez stelae (decoration 4)
sort(named_elements(lgrph[[4]], focus = "edges", nd.var="type"))
> [1] "bouclier-=-chariot_char" "bouclier-=-lance"
> [3] "bouclier-=-personnage" "casque-+-rivet"
> [5] "casque-=-epee" "casque-=-miroir"
> [7] "chariot_char-+-cheval" "chariot_char-+-cheval#"
> [9] "chariot_char-+-roue" "chariot_char-+-roue#"
> [11] "chariot_char-=-personnage" "epee-=-lance"
> [13] "epee-=-miroir" "lance-=-personnage"
Employed with the basic R functions for loop (lapply()), counts (table()) and order (order()), and removing the the suffix # this function can be used to count the different types of edges. Here, the most represented ones:
all.edges <- lapply(lgrph,
function(x) named_elements(x, focus = "edges",
nd.var="type"))
edges.list <- gsub("#", "", unlist(all.edges))
all.edges.ct <- as.data.frame(table(edges.list))
all.edges.ct <- all.edges.ct[with(all.edges.ct, order(-Freq)), ]
knitr::kable(head(all.edges.ct), row.names = F) %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12)
| edges.list | Freq |
|---|---|
| chariot_char-+-cheval | 4 |
| chariot_char-+-roue | 4 |
| bouclier-=-epee | 3 |
| bouclier-=-lance | 3 |
| bouclier-=-chariot_char | 2 |
| bouclier-=-fibule | 2 |
The normal edges are undirected: 1-=-2 is equal to 2-=-1, node 1 and node 2 are two different main nodes. Differents main nodes considerated as contemporaneous and close one with another, shared an edge with the value = (textual notation: -=-) for their type. By convention, these edges are called normal and displayed with a plain line.
nds.df <- read_nds(site = "Brozas",
decor = "Brozas",
doss = dataDir)
eds.df <- read_eds(site = "Brozas",
decor = "Brozas",
doss = dataDir)
img.graph.id <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Brozas",
decor = "Brozas",
lbl.size = 2,
doss = dataDir)
img.graph.type <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Brozas",
decor = "Brozas",
lbl.size = 2,
nd.var = 'type',
doss = dataDir)
magick::image_append(c(magick::image_read(img.graph.id),
magick::image_read(img.graph.type)))
Brozas stelae (decoration 3) with only normal edges: all the composition seems contemporaneous
For example, the normal edge between the nodes 5 and 6 (respectively the shield and a sword) is:
When a node is an attribute of another, edges are identified with a + (textual notation: -+-) and displayed with a dashed line. The attribute edges are directed: 1-+-2 is not equal to 2-+-1, 1-+-2 means that node 1 is the main node and node 2 is one of its attribute node. For example, at the bottom of the Zarza de Montsanchez stelae (decoration 4), the main node 7 (chariot) is connected with four (4) nodes attributes:
two horses (cheval): 8 and 9
two wheels (roue): 10 and 11
nds.df <- read_nds(site = "Zarza de Montanchez",
decor = "Zarza De Montanchez",
doss = dataDir)
eds.df <- read_eds(site = "Zarza de Montanchez",
decor = "Zarza De Montanchez",
doss = dataDir)
img.graph.id <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Zarza de Montanchez",
decor = "Zarza De Montanchez",
lbl.size = 2,
doss = dataDir)
img.graph.type <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Zarza de Montanchez",
decor = "Zarza De Montanchez",
lbl.size = 2,
nd.var = 'type',
doss = dataDir)
magick::image_append(c(magick::image_read(img.graph.id),
magick::image_read(img.graph.type)))
Zarza De Montanchez stelae (decoration 4) showing normal and attribute edges
When a node overlaps another – or is more recent than another – edges are identified with a > (textual notation: ->-) and displayed with a blue plain line. The overlap edges are directed: 1->-2 is not equal to 2->-1, 1->-2 means that node 1 overlaps node 2. For example, the Ibahernando stelae shows a latin writing (écriture) overlapping a spear (lance) and a shield (bouclier).
nds.df <- read_nds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
eds.df <- read_eds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
img.graph.id <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Ibahernando",
decor = "Ibahernando",
lbl.size = 2,
doss = dataDir)
img.graph.type <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Ibahernando",
decor = "Ibahernando",
nd.var = 'type',
lbl.size = 2,
doss = dataDir)
magick::image_append(c(magick::image_read(img.graph.id),
magick::image_read(img.graph.type)))
Ibahernando stelae (decoration 5) showing normal and overlap edges
These overlappings can be manage with the contemp_nds() function (see the section Contemporaneous contents)
Decoration graphs are constructed from nodes and the edges. As a RNG graph, graphs are 1-component: each decoration graph covers all the GUs of the decoration. The functions of the decorr package provide basic requirements to manage these nodes data (.csv, .tsv or .shp) and edges data (.csv, .tsv or .shp) to create graphs, to plot and to compare them, to select contemporaneous GUs compositions
cat(ls("package:decorr"), sep="\n")
> contemp_nds
> eds_compar
> labels_shadow
> list_compar
> list_dec
> named_elements
> nds_compar
> plot_compar
> plot_dec_grph
> read_eds
> read_nds
> same_elements
The graphical functions, plot_dec_grph() and plot_compar() allow different choices for the color and the size of the nodes, edges or labels. For example, nodes and edges of Cerro Muriano 1, and the field type used for the label can be selected instead of the default id field (identifier of the node)
nds.df <- read_nds(site = "Cerro Muriano", decor = "Cerro Muriano 1", doss = dataDir)
eds.df <- read_eds(site = "Cerro Muriano", decor = "Cerro Muriano 1", doss = dataDir)
img.graph <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Cerro Muriano",
decor = "Cerro Muriano 1",
nd.var = 'type',
lbl.size = 1.8,
doss = dataDir)
magick::image_read(img.graph)
Cerro Muriano 1 stelae (decoration 1) with the type of each GU
A new field, long_cm is added to Cerro Muriano 1 nodes and the graph is replot on this field instead of the type field, with brown colors and label bigger sizes
nds.df <- read_nds(site = "Cerro Muriano", decor = "Cerro Muriano 1", doss = dataDir)
nds.df$long_cm <- c(47, 9, 47, 18, 7, 3, 13)
eds.df <- read_eds(site = "Cerro Muriano", decor = "Cerro Muriano 1", doss = dataDir)
img.graph <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Cerro Muriano",
decor = "Cerro Muriano 1",
nd.var = 'long_cm',
nd.color = "brown",
lbl.color = "brown",
ed.color = "brown",
lbl.size=2.5,
doss = dataDir)
magick::image_read(img.graph)
Cerro Muriano 1 stelae (decoration 1) with the maximum length (in cm) of each GU
Between all the graphs, or between a pair of graphs, elements of the graphs (nodes and edges) can be compared with the same_elements() and plot_compar() functions
same_elements() function permits to count each common elements between n graphs
plot_compar() function shows a graphical output for these coomon elements
By default, between a pairwise of decorations, common nodes and edges are displayed in red, but their colors – and other graphical parameters – can be modified. When not all GUs are contemporaneous one with another, the non-contemporaneous ones can be removed with the contemp_nds() function.
A classic study in archaeological research is to count the common nodes between decoration pairwises. This can be done with the same_elements() function with a node focus (focus = "nodes"), and considering for example their type.
imgs_path <- paste0(dataDir, "/imgs.tsv")
nodes_path <- paste0(dataDir, "/nodes.tsv")
edges_path <- paste0(dataDir, "/edges.tsv")
imgs <- read.table(imgs_path, sep="\t", stringsAsFactors = FALSE)
nodes <- read.table(nodes_path, sep="\t", stringsAsFactors = FALSE)
edges <- read.table(edges_path, sep="\t", stringsAsFactors = FALSE)
lgrph <- list_dec(imgs, nodes, edges)
df.same_nodes <- same_elements(lgrph,
focus = "nodes",
nd.var = "type")
diag(df.same_nodes) <- cell_spec(diag(df.same_nodes),
font_size = 9)
knitr::kable(df.same_nodes, row.names = TRUE, escape = F,
caption = "count of common nodes between decorations") %>%
column_spec(1, bold=TRUE) %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12)
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| 1 | 7 | 2 | 3 | 4 | 2 |
| 2 | 2 | 12 | 5 | 9 | 3 |
| 3 | 3 | 5 | 6 | 4 | 3 |
| 4 | 4 | 9 | 4 | 12 | 3 |
| 5 | 2 | 3 | 3 | 3 | 4 |
The results of same_elements() is a symetrical dataframe where row names and column headers are the identifiers of the decorations:
cells of the dataframe show the total number of common nodes by decoration pairwises
the diagonal of the dataframe shows the total number of nodes or edges of a given decoration
Regarding the node type variable, the decoration 4 has twelve (12) nodes, has nine (9) common nodes with the decoration 2, and has four (4) common nodes with the decoration 3. This matrix can be used for further clustering analysis
To compare graphically the decorations 2, 3 and 4 on the type variable:
type variable is pasted to the list_compar() functionplot_compar() functiondec.to.compare <- c(2, 3, 4)
imgs_path <- paste0(dataDir, "/imgs.tsv")
nodes_path <- paste0(dataDir, "/nodes.tsv")
edges_path <- paste0(dataDir, "/edges.tsv")
imgs <- read.table(imgs_path, sep="\t", stringsAsFactors = FALSE)
nodes <- read.table(nodes_path, sep="\t", stringsAsFactors = FALSE)
edges <- read.table(edges_path, sep="\t", stringsAsFactors = FALSE)
lgrph <- list_dec(imgs, nodes, edges)
g.compar <- list_compar(lgrph, nd.var = "type")
nds_compar <- plot_compar(listg = g.compar,
graph2 = dec.to.compare,
focus = "nodes",
nd.size = c(0.5, 1.5),
doss = dataDir)
knitr::include_graphics(nds_compar)
The function creates an image for each pairwise of stelae contained in the dec.to.compare variable (2, 3, 4), with a focus on nodes (focus = "nodes"). That is to say \(\frac{n!}{(n-2)!2!}\) pairwise comparisons, when n is the number of compared decoration. Then, to compare each pairwsise of the five (5) decorations of the training dataset, there is 10 possible comparisons.
A not so classic study in archaeological research is to count the common edges between decorations pairwise. The same_elements() function with an edge focus (focus = "edges") and considering the type of the nodes
imgs_path <- paste0(dataDir, "/imgs.tsv")
nodes_path <- paste0(dataDir, "/nodes.tsv")
edges_path <- paste0(dataDir, "/edges.tsv")
imgs <- read.table(imgs_path, sep="\t", stringsAsFactors = FALSE)
nodes <- read.table(nodes_path, sep="\t", stringsAsFactors = FALSE)
edges <- read.table(edges_path, sep="\t", stringsAsFactors = FALSE)
lgrph <- list_dec(imgs, nodes, edges)
df.same_edges <- same_elements(lgrph, nd.var = "type", focus = "edges")
diag(df.same_edges) <- cell_spec(diag(df.same_edges),
font_size = 9)
knitr::kable(df.same_edges, row.names = TRUE, escape = F,
caption = "count of common edges between decorations") %>%
column_spec(1, bold = TRUE) %>%
kableExtra::kable_styling(full_width = FALSE, position = "center", font_size=12)
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| 1 | 8 | 0 | 1 | 2 | 0 |
| 2 | 0 | 15 | 3 | 7 | 1 |
| 3 | 1 | 3 | 10 | 1 | 2 |
| 4 | 2 | 7 | 1 | 14 | 1 |
| 5 | 0 | 1 | 2 | 1 | 4 |
In this dataframe:
cells show the total number of common edges by decorations
the diagonal of the dataframe shows is the total number of a given decoration
Here, the decoration 2 has fiftheen (15) edges and shares three (3) common edges with the decoration 3. To show them, and the decoration 4, we use the list_compar() function on the same variable (type) and the plot_compar() function with an edge focus (focus = "edges").
dec.to.compare <- c(2, 3, 4)
imgs_path <- paste0(dataDir, "/imgs.tsv")
nodes_path <- paste0(dataDir, "/nodes.tsv")
edges_path <- paste0(dataDir, "/edges.tsv")
imgs <- read.table(imgs_path, sep="\t", stringsAsFactors = FALSE)
nodes <- read.table(nodes_path, sep="\t", stringsAsFactors = FALSE)
edges <- read.table(edges_path, sep="\t", stringsAsFactors = FALSE)
lgrph <- list_dec(imgs, nodes, edges) # call function
g.compar <- list_compar(lgrph, nd.var = "type")
eds_compar <- plot_compar(listg = g.compar,
graph2 = dec.to.compare,
focus = "edges",
nd.size = c(0.5, 1.7),
doss = dataDir)
# open image
knitr::include_graphics(eds_compar)
This matrix can be used for further clustering analysis
It occurs that some nodes are non-contemporaneous one with the others, like for the Ibahernando stelae. This stelae have been found reused as a funerary steale during Roman times with latin insciption “Alloquiu protaeidi.f hece. stitus”: Alluquio, son of Protacido, lies here (Basch 1966).
GIS view. The Ibahernando stelae (decoration 5)
The writing (ecriture, node 1) has been carved over a spear (lance, node 2) and overlaps partially a V-notched shield (bouclier, node 3). The edges between node 1 and node 2, and the edge between node 1 and node 3, are overlap edges
nds.df <- read_nds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
eds.df <- read_eds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
img.graph <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Ibahernando",
decor = "Ibahernando",
lbl.size = 2,
doss = dataDir)
magick::image_read(img.graph)
Ibahernando stelae (decoration 5) with only normal edges, node 1 overlaps node 2 and node 3
In this case, the non-contemporaneous layers of decoration, both for nodes and edges, should be removed before the comparison process. To do so, the original graph (1-component) will be splitted into different sub-graphs (n-component) by selecting > edges (see overlap edges). The studied graph component will be retrieved with the component membership of a selected node in the contemp_nds() function parameters.
To study only the Late Bronze Age iconographic layer of the Ibahernando steale, we can choose the Late Bronze Age node 4, the figure of sword (epee) dated to the middle and final stages of Late Bronze Age (ca 1250-950 BC). This node is supposed to be contemporaneous to node 2 and node 3 (see normal edges)
selected.nd <- 4
nds.df <- read_nds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
eds.df <- read_eds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
l_dec_df <- contemp_nds(nds.df, eds.df, selected.nd)
Ibahernando <- plot_dec_grph(nodes = nds.df,
edges = eds.df,
site = "Ibahernando",
decor = "Ibahernando",
nd.var = "type",
lbl.color = "brown",
lbl.size = 2.2,
doss = dataDir)
Ibahernando.img <- magick::image_read(Ibahernando)
Ibahernando.contemp <- plot_dec_grph(nodes = l_dec_df[[1]],
edges = l_dec_df[[2]],
site = "Ibahernando",
decor = "Ibahernando",
nd.var = "type",
lbl.color = "brown",
lbl.size = 2.2,
doss = dataDir)
Ibahernando.contemp.img <- magick::image_read(Ibahernando.contemp)
magick::image_append(c(Ibahernando.img, Ibahernando.contemp.img))
Ibahernando stelae before and after the selection of node 4 (sword) graph component
At the opposite, epigraphists will study only the iconographic layer of the latin writing. By selecting the node 1 (ecriture), only the component of this node will be selected
selected.nd <- 1
nds.df <- read_nds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
eds.df <- read_eds(site = "Ibahernando",
decor = "Ibahernando",
doss = dataDir)
l_dec_df <- contemp_nds(nds.df, eds.df, selected.nd)
img.graph <- plot_dec_grph(nodes = l_dec_df[[1]],
edges = l_dec_df[[2]],
site = "Ibahernando",
decor = "Ibahernando",
nd.var = "type",
lbl.size = 2,
lbl.color = "brown",
doss = dataDir)
magick::image_read(img.graph)
Ibahernando stelae after the selection of node 1 (writing) graph component
Plasticity of graph theory and facilities coming from the GIS database make decorr package usefull to manage, plot and compare large series of iconographical contents.
Beside graphical functions permitting to highlight common elements (nodes and edges) between decorations, the package allows to prepare data for unsupervised classification, like Hierarchical clustering with the dist() and hclust()
par(mfrow=c(1, 2))
df.same_edges <- same_elements(lgrph, "type", "edges")
df.same_nodes<- same_elements(lgrph, "type", "nodes")
dist.nodes <- dist(as.matrix(df.same_nodes), method = "euclidean")
dist.edges <- dist(as.matrix(df.same_edges), method = "euclidean")
hc.nds <- hclust(dist.nodes, method = "ward.D")
hc.eds <- hclust(dist.edges, method = "ward.D")
plot(hc.nds, main = "common nodes", cex = .8)
plot(hc.eds, main = "common edges", cex = .8)
Classifications themselves can be compared one to another
suppressPackageStartupMessages(library(dendextend))
suppressPackageStartupMessages(library(dplyr))
par(mfrow=c(1, 2))
dend.nds <- as.dendrogram (hc.nds)
dend.eds <- as.dendrogram (hc.eds)
dendlist(dend.nds, dend.eds) %>%
untangle(method = "step1side") %>%
tanglegram(columns_width = c(6, 1, 6),
main = paste0("comparison between decorations clustering on commons nodes\n",
"and decorations clustering on common edges"),
main_left = "common nodes",
main_right = "common edges",
lab.cex = 1.3,
cex_main = 1.8,
highlight_branches_lwd = F)
In both clusterings, Brozas stelae (decoration 3) and Ibahernando stelae (decoration 5) are the ones having the most important proximities (ie the lesser euclidian distance)
dec.to.compare <- c(3, 5)
g.compar <- list_compar(lgrph, nd.var = "type")
nds_compar <- plot_compar(listg = g.compar,
graph2 = dec.to.compare,
focus = "nodes",
nd.size = c(0.5, 1.7),
doss = dataDir)
eds_compar <- plot_compar(listg = g.compar,
graph2 = dec.to.compare,
focus = "edges",
nd.size = c(0.5, 1.7),
doss = dataDir)
knitr::include_graphics(nds_compar)
knitr::include_graphics(eds_compar)
Here, the comparisons have only been done on the different type of (ie, graphical units, GUs) with the variable type (nd.var = "type"). But, if a new column is added to the node dataframe or shapefile, the study can also integrate the technique (nd.var = "technique") or any other controled vocabularies. For example, two GUs displayed on the Brozas steale have been made with incisions (g_inc): the fibula (fibula de codo tipo Huelva, ca 1050-950 BC) and the comb
GIS view. The Brozas stelae (decoration 1)
Graph theory permit to construct tree structures for controled vocabulary (eg. the different types of GUs). These structures allow generalization processes (up to the parent level) and specification processes (down to the children level). For example, a sword and a spear belongs both to the weapons group (sub-group offensive weapons), a shield belongs to the weapons group (sub-group defensive weapons), etc.
par(mar=c(0.1, 0.1, 0.1, 0.1) )
g <- graph_from_literal(objects-+weapons,
objects-+personnal_item,
weapons-+offensive_weapons,
weapons-+defensive_weapons,
offensive_weapons-+spear,
offensive_weapons-+sword,
defensive_weapons-+shield,
defensive_weapons-+helmet,
personnal_item-+miror,
personnal_item-+comb)
plot(g,
layout = layout.reingold.tilford,
vertex.color="white",
vertex.frame.color="white",
vertex.size=40,
vertex.label.cex=0.8,
vertex.label.color="black",
vertex.label.family = "sans",
edge.arrow.size=0.5,
margin = -0.1
)
Such a formalism can be used to weight the differences between nodes, to conduct analysis with different level of precision or to overcome idiosynchratic typologies issues.
Using the overlap edge notation, tree structure can also be used to construct relative chronology diagrams, like a Harris matrix. For example, with the data.tree package and the Ibahernado stelae
library(data.tree)
lgrph <- list_dec(imgs, nodes, edges)
edges.iba <- igraph::as_data_frame(lgrph[[5]], what="edges")
overlap.nodes <- unlist(unique(edges.iba[edges.iba$type == ">", "from"]))
overlap.nodes <- unique(as.character(overlap.nodes))
contemp.nodes <- unlist(unique(edges.iba[edges.iba$type == "=", c("from","to")]))
contemp.nodes <- unique(as.character(contemp.nodes))
df.stratig <- data.frame(over = rep(overlap.nodes, length(contemp.nodes)),
under = contemp.nodes)
df.stratig$pathString <- paste(lgrph[[5]]$decor,
df.stratig$over,
df.stratig$under,
sep = "/")
superpo <- as.Node(df.stratig)
print(superpo)
> levelName
> 1 Ibahernando
> 2 °--1
> 3 ¦--2
> 4 ¦--3
> 5 °--4
Alexander, Craig. 2008. “The Bedolina Map – an Exploratory Network Analysis.” In Layers of Perception. Proceedings of the 35th International Conference on Computer Applications and Quantitative Methods in Archaeology (Caa), Berlin, 2.-6. April 2007, edited by A. Posluschny, K. Lambers, and I. Herzog, 366–71. Koll. Vor- u. Frühgesch. https://doi.org/https://doi.org/10.11588/propylaeumdok.00000512.
Basch, Martı́n Almagro. 1966. Las Estelas Decoradas Del Suroeste Peninsular. Vol. 8. Editorial CSIC-CSIC Press.
Dı́az-Guardamino Uribe, Marta. 2010. “Las Estelas Decoradas En La Prehistoria de La Penı́nsula Ibérica.” PhD thesis, Universidad Complutense de Madrid, Servicio de Publicaciones. https://eprints.ucm.es/11070/1/T32200.pdf.
Huet, Thomas. 2018. “Geometric Graphs to Study Ceramic Decoration.” In Exploring Oceans of Data, Proceedings of the 44th Conference on Computer Applications and Quantitative Methods in Archaeology, Caa 2016, edited by Mieko Matsumoto and Espen Uleberg, 311–24. Archaeopress. https://hal.archives-ouvertes.fr/hal-02913656.
Huet, Thomas, and Craig Alexander. 2015. “Méthodes Informatiques Pour L’étude Des Gravures Rupestres : Les Exemples Du Valcamonica (Italie) et Du Mont Bego (France).” In Recherches Sur L’âge Du Bronze, Nouvelles Approches et Perspectives, Actes de La Journée d’étude de L’Association Pour La Promotion Des Recherches Archéologiques Sur L’âge Du Bronze, 28 Février 2014, Saint-Germain-En-Laye, Bulletin de L’APRAB, Suppl. N° 1, edited by M. Nordez, L. Rousseau, and M. Cervel, 15–29. Nantes. https://www.researchgate.net/publication/347437308_Methodes_informatiques_pour_l'etude_des_gravures_rupestres_les_exemples_du_Valcamonica_Italie_et_du_mont_Bego_France.